# coding=utf-8
import os
import time
from ctypes import *
from datetime import datetime

# 导入海康SDK封装
from HCNetSDK import (
    NET_DVR_Init, NET_DVR_Cleanup, NET_DVR_GetLastError,
    NET_DVR_USER_LOGIN_INFO, NET_DVR_DEVICEINFO_V40,
    NET_DVR_Login_V40, NET_DVR_Logout,
    NET_DVR_VOD_PARA, NET_DVR_STREAM_INFO,
    NET_DVR_PLAYCOND, NET_DVR_TIME,
    NET_DVR_PlayBackByTime_V50, NET_DVR_PlayBackControl_V40,
    NET_DVR_StopPlayBack,
    NET_DVR_SetConnectTime, NET_DVR_SetReconnect,
    NET_DVR_SetPlayDataCallBack_V40, REALDATACALLBACK,
    NET_DVR_PLAYSTART, NET_DVR_SYSHEAD, NET_DVR_STREAMDATA,
    NET_DVR_LOCAL_SDK_PATH, NET_SDK_INIT_CFG_TYPE
)

# 导入播放控制封装
from PlayCtrl import (
    PlayM4_GetPort, PlayM4_OpenStream, PlayM4_SetDecCallBackEx,
    PlayM4_Play, PlayM4_InputData, PlayM4_Stop,
    PlayM4_CloseStream, PlayM4_FreePort, DECCBFUNWIN
)

class HikvisionRecorder:
    def __init__(self):
        """初始化录像操作对象"""
        self.hikSDK = None
        self.playM4SDK = None
        self.user_id = -1  # 登录句柄
        self.playback_handle = -1  # 回放句柄
        self.playback_port = c_long(-1)  # 播放端口
        self.base_path = os.getcwd()  # 基础路径
        
        # 加载SDK库
        self._load_sdk()
        
    def _load_sdk(self):
        """加载海康威视SDK库"""
        try:
            # 根据操作系统加载不同的SDK库
            if os.name == 'nt':  # Windows系统
                self.hikSDK = WinDLL('./lib/HCNetSDK.dll')
                self.playM4SDK = WinDLL('./lib/PlayCtrl.dll')
            else:  # Linux系统
                self.hikSDK = CDLL('./lib/libhcnetsdk.so')
                self.playM4SDK = CDLL('./lib/libPlayCtrl.so')
                
            print("SDK库加载成功")
        except OSError as e:
            print(f"SDK库加载失败: {e}")
            exit(1)
    
    def init_sdk(self):
        """初始化SDK环境"""
        # 初始化SDK
        if not self.hikSDK.NET_DVR_Init():
            error = self.hikSDK.NET_DVR_GetLastError()
            print(f"SDK初始化失败, 错误码: {error}")
            return False
        
        print("SDK初始化成功")
        
        # 设置连接超时和重连参数
        self.hikSDK.NET_DVR_SetConnectTime(2000, 1)
        self.hikSDK.NET_DVR_SetReconnect(10000, True)
        
        # 设置日志路径
        log_path = os.path.join(self.base_path, "logs").encode()
        self.hikSDK.NET_DVR_SetLogToFile(3, log_path, False)
        
        return True
    
    def set_sdk_paths(self):
        """设置SDK依赖库路径"""
        # 创建路径结构体
        sdk_path = NET_DVR_LOCAL_SDK_PATH()
        lib_path = os.path.join(self.base_path, "lib").encode()
        sdk_path.sPath = lib_path
        
        # 设置SDK组件路径
        if not self.hikSDK.NET_DVR_SetSDKInitCfg(
            NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_SDK_PATH.value,
            byref(sdk_path)
        ):
            error = self.hikSDK.NET_DVR_GetLastError()
            print(f"设置SDK路径失败, 错误码: {error}")
    
    def login_device(self, ip, port, username, password):
        """登录设备"""
        # 准备登录信息
        login_info = NET_DVR_USER_LOGIN_INFO()
        login_info.bUseAsynLogin = 0  # 同步登录
        login_info.sDeviceAddress = ip.encode()  # 设备IP
        login_info.wPort = port  # 设备端口
        login_info.sUserName = username.encode()  # 用户名
        login_info.sPassword = password.encode()  # 密码

        # 添加加密登录参数
        login_info.byUseTransport = 1  # 启用加密传输
        login_info.byLoginMode = 0     # 密码登录模式
        
        # 设备信息结构体
        device_info = NET_DVR_DEVICEINFO_V40()
        
        # 执行登录
        self.user_id = self.hikSDK.NET_DVR_Login_V40(
            byref(login_info), byref(device_info))
        print(f"登录返回值: {self.user_id} (类型: {type(self.user_id)})")
        if self.user_id < 0:
            error = self.hikSDK.NET_DVR_GetLastError()
            print(f"登录失败, 错误码: {error}")
            return False
        
        # 获取设备序列号
        serial_bytes = bytes(device_info.struDeviceV30.sSerialNumber)
        serial_number = serial_bytes.decode('utf-8', errors='ignore').rstrip('\x00')
        print(f"登录成功, 设备序列号: {serial_number}")

        return True
        
    def logout_device(self):
        """登出设备"""
        if self.user_id >= 0:
            self.hikSDK.NET_DVR_Logout(self.user_id)
            self.user_id = -1
            print("设备已登出")
    
    def playback_by_time(self, channel, start_time, end_time, duration=15):
        """
        按指定时间范围回放录像
        :param channel: 通道号 (固定为19)
        :param start_time: 开始时间 (datetime对象)
        :param end_time: 结束时间 (datetime对象)
        :param duration: 回放持续时间 (秒)
        :return: 是否成功
        """
        try:
            # 验证时间范围有效性
            if start_time >= end_time:
                raise ValueError("开始时间必须早于结束时间")
            
            # 创建回放参数结构体
            vod_params = NET_DVR_VOD_PARA()
            vod_params.dwSize = sizeof(NET_DVR_VOD_PARA)
            vod_params.hWnd = 0  # 窗口句柄
            
            print("vod_params.dwSize = ",vod_params.dwSize)
            print("vod_params.hWnd = ",vod_params.hWnd)

            # 设置流信息
            stream_info = NET_DVR_STREAM_INFO()
            stream_info.dwSize = sizeof(NET_DVR_STREAM_INFO)
            stream_info.dwChannel = channel
            stream_info.byID = 0
            stream_info.dwStreamType = 0  # 主码流
            stream_info.dwLinkMode = 1    # TCP私有加密
            vod_params.struIDInfo = stream_info
            
            # 填充开始和结束时间
            def fill_dvr_time(dt, dvr_time):    
                """将datetime对象转换为SDK时间结构"""
                dvr_time.wYear = dt.year
                dvr_time.byMonth = dt.month
                dvr_time.byDay = dt.day
                dvr_time.byHour = dt.hour
                dvr_time.byMinute = dt.minute
                dvr_time.bySecond = dt.second
            
            fill_dvr_time(start_time, vod_params.struBeginTime)
            fill_dvr_time(end_time, vod_params.struEndTime)
            
            # 获取播放端口
            self.playback_port = c_long(-1)
            if not self.playM4SDK.PlayM4_GetPort(byref(self.playback_port)):
                raise RuntimeError("播放端口获取失败")
            print(f"成功获取播放端口: {self.playback_port.value}")
           
            # 启动回放
            self.playback_handle = self.hikSDK.NET_DVR_PlayBackByTime_V50(
                self.user_id, byref(vod_params))
            print(f"self.playback_handle: {self.playback_handle}")
            
            if self.playback_handle < 0:
                error = self.hikSDK.NET_DVR_GetLastError()
                raise RuntimeError(f"回放启动失败: {self._get_error_msg(error)}")
            
            print(f"回放启动成功 (句柄: {self.playback_handle})")

            # 设置数据回调函数
            @REALDATACALLBACK
            def real_data_callback(lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):
                """实时数据回调处理"""
                # 系统头数据
                if dwDataType == NET_DVR_SYSHEAD:
                    if not self.playM4SDK.PlayM4_OpenStream(
                        self.playback_port, pBuffer, dwBufSize, 2*1024*1024):
                        print("流打开失败")
                # 流数据
                elif dwDataType == NET_DVR_STREAMDATA:
                    if not self.playM4SDK.PlayM4_InputData(
                        self.playback_port, pBuffer, dwBufSize):
                        print("数据输入失败")

            self.func_real_data_callback = real_data_callback
            if not self.hikSDK.NET_DVR_SetPlayDataCallBack_V40(
                self.playback_handle, self.func_real_data_callback, None):
                print("回调设置失败 (不影响基础回放)")

            # 开始播放
            if not self.hikSDK.NET_DVR_PlayBackControl_V40(
                self.playback_handle, NET_DVR_PLAYSTART, None, 0, None, None):
                raise RuntimeError("播放控制命令失败")
            print("播放控制命令发送成功")

            # 播放监控
            print(f"\n回放进行中 (将持续 {duration} 秒)...")
            start_play = time.time()
            while time.time() - start_play < duration:
                time.sleep(1)
                # 获取并显示播放进度
                progress = c_long()
                if self.hikSDK.NET_DVR_PlayBackControl_V40(
                    self.playback_handle, NET_DVR_PLAYGETPOS, 
                    None, 0, byref(progress), sizeof(c_long)):
                    print(f"当前进度: {progress.value}%", end='\r')
            
            print("\n回放完成")
            return True

        except Exception as e:
            print(f"\n回放过程中断: {str(e)}")
            return False
        finally:
            self.stop_playback()

    def _get_error_msg(self, error_code):
        """获取错误信息描述"""
        try:
            err_buf = create_string_buffer(256)
            if hasattr(self.hikSDK, 'NET_DVR_GetErrorMsg'):
                if self.hikSDK.NET_DVR_GetErrorMsg(error_code, err_buf, 256):
                    return err_buf.value.decode('gbk', 'ignore')
            return f"未知错误 (代码: {error_code})"
        except:
            return f"无法获取错误详情 (代码: {error_code})"

    def stop_playback(self):
        """停止回放并释放资源"""
        if self.playback_handle >= 0:
            # 停止回放
            self.hikSDK.NET_DVR_StopPlayBack(self.playback_handle)
            self.playback_handle = -1
            
            # 停止播放库并释放资源
            if self.playback_port.value > -1:
                self.playM4SDK.PlayM4_Stop(self.playback_port)
                self.playM4SDK.PlayM4_CloseStream(self.playback_port)
                self.playM4SDK.PlayM4_FreePort(self.playback_port)
                self.playback_port = c_long(-1)
            print("回放已停止")
    
    def cleanup(self):
        """清理SDK资源"""
        self.hikSDK.NET_DVR_Cleanup()
        print("SDK资源已清理")

if __name__ == '__main__':
    # ========== 设备配置 ==========
    DEVICE_IP = "192.168.10.200"
    DEVICE_PORT = 8000
    USERNAME = "admin"
    PASSWORD = "aa147258"
    CHANNEL = 19  # 固定通道号为19
    
    # 创建录像操作对象
    recorder = HikvisionRecorder()
    recorder.set_sdk_paths()
    
    if not recorder.init_sdk():
        exit(1)
    
    if not recorder.login_device(DEVICE_IP, DEVICE_PORT, USERNAME, PASSWORD):
        recorder.cleanup()
        exit(1)
    
    try:
        # ========== 回放参数设置 ==========
        now = datetime.now()
        # 回放昨天当前时间前一小时的录像 (持续10分钟)
        start_time = datetime(now.year, now.month, now.day-1, now.hour-1, 0)
        end_time = datetime(now.year, now.month, now.day-1, now.hour-1, 10)
        
        # 执行回放操作 (持续15秒)
        if recorder.playback_by_time(CHANNEL, start_time, end_time, duration=15):
            print("回放操作成功完成")
    
    finally:
        # 确保资源清理
        recorder.logout_device()
        recorder.cleanup()